/* 
 * File:   Xpack.cpp
 * Author: minaev
 * 
 * Created on 30 Октябрь 2013 г., 10:07
 */

#include "Xpack.h"
#include "Xservice.h"
#include "Xmsg.h"
#include <stddef.h>
#include <stdio.h>

Xpack::Xpack() {
    this->length = 0;
    this->type = 0xff; //unknow
    this->data = NULL;
    this->crc = 0;
}

Xpack::Xpack(char* frm) {
    this->pack(frm);
}

Xpack::~Xpack() {
    if (data != NULL) {
        delete [] data;
    }
}

/**
 * Set values of pack from receive buffer
 * @param frm - receive buffer
 * @return 0 - success, else - error
 * 1 - start delimiter != 0x7e
 */
int Xpack::pack(char* frm) {
    if (frm[0] != 0x7e) {
        return 1;
    }
    this->length = ((frm[1] << 8) | frm[2]);
    this->type = frm[3];
    this->data = new char[length];

    for (int i = 0; i < length; i++) {
        this->data[i] = frm[i + 3];
    }

    this->crc = frm[this->length + 3];
}

char* Xpack::toBytes() {
    static char* buf = NULL;

    if (this->length == 0) {
        return NULL;
    }

    if (buf != NULL) {
        delete [] buf;
    }
    buf = new char[this->length + 4];
    buf[0] = 0x7e;
    buf[1] = (this->length >> 8) & 0xff;
    buf[2] = this->length & 0xff;
    for (int i = 0; i<this->length; i++) {
        buf[i + 3] = this->data[i];
    }
    buf[length + 3] = this->crc;
    return buf;
}

uint8_t Xpack::getByte(uint16_t indx){
    if(this->length<=indx){
        return 0;
    }
    return (uint8_t)this->data[indx];
}

uint16_t Xpack::getLength() {
    return this->length;
}

uint8_t Xpack::getType() {
    return this->type;
}

char* Xpack::getData() {
    return this->data;
}

uint8_t Xpack::getCRC() {
    return this->crc;
}

bool Xpack::checkCRC() {
    return this->crc == this->calcCRC();
}

uint8_t Xpack::calcCRC() {
    static unsigned int lcrc;
    lcrc = 0;
    for (int i = 0; i<this->length; i++) {
        lcrc += this->data[i];
    }
    lcrc = 0xff - (lcrc & 0xff);
    return lcrc;
}

void Xpack::setCRC() {
    this->crc = this->calcCRC();
}

void Xpack::setType() {
    if (this->length > 0) {
        this->type = this->data[0];
    }
}

void Xpack::setInPack(const char* buf, int len, int offset) {
    if (this->length >= offset + len && offset + len > 0) {
        for (int i = 0; i < len; i++) {
            this->data[offset + i] = buf[i];
        }
    }
    this->setCRC();
    this->setType();
}


void Xpack::setInPack(const char ch, int offset) {
    this->setInPack(&ch, 1, offset);
}

void Xpack::setInPack(const uint8_t num, int offset) {
    this->setInPack((char) num, offset);
}

void Xpack::setInPack(const uint16_t num, int offset) {
    static char buf[2];
    buf[0] = (num >> 8) & 0xff;
    buf[1] = num & 0xff;
    this->setInPack(buf, 2, offset);
}

void Xpack::setInPack(const uint32_t num, int offset) {
    static char buf[4];
    buf[0] = (num >> 24) & 0xff;
    buf[1] = (num >> 16) & 0xff;
    buf[2] = (num >> 8) & 0xff;
    buf[3] = num & 0xff;
    this->setInPack(buf, 4, offset);
}

void Xpack::setInPack(const uint64_t num, int offset) {
    static char buf[8];
    buf[0] = (num >> 56) & 0xff;
    buf[1] = (num >> 48) & 0xff;
    buf[2] = (num >> 40) & 0xff;
    buf[3] = (num >> 32) & 0xff;
    buf[4] = (num >> 24) & 0xff;
    buf[5] = (num >> 16) & 0xff;
    buf[6] = (num >> 8) & 0xff;
    buf[7] = num & 0xff;
    this->setInPack(buf, 8, offset);
}

void Xpack::addToPack(const char* buf, int len) {
    static char* olddata;
    olddata = this->data;
    this->data = new char[this->length + len];
    for (int i = 0; i<this->length; i++) {
        this->data[i] = olddata[i];
    }
    for (int i = 0; i < len; i++) {
        this->data[i + this->length] = buf[i];
    }
    this->length += len;
    this->setCRC();
    this->setType();
    if (olddata != NULL) {
        delete [] olddata;
    }
}

void Xpack::addToPack(const char ch) {
    this->addToPack(&ch, 1);
}

void Xpack::addToPack(const uint8_t num) {
    this->addToPack((char) num);
}

void Xpack::addToPack(const uint16_t num) {
    static char buf[2];
    buf[0] = (num >> 8) & 0xff;
    buf[1] = num & 0xff;
    this->addToPack(buf, 2);
}

void Xpack::addToPack(const uint32_t num) {
    static char buf[4];
    buf[0] = (num >> 24) & 0xff;
    buf[1] = (num >> 16) & 0xff;
    buf[2] = (num >> 8) & 0xff;
    buf[3] = num & 0xff;
    this->addToPack(buf, 4);
}

void Xpack::addToPack(const uint64_t num) {
    static char buf[8];
    buf[0] = (num >> 56) & 0xff;
    buf[1] = (num >> 48) & 0xff;
    buf[2] = (num >> 40) & 0xff;
    buf[3] = (num >> 32) & 0xff;
    buf[4] = (num >> 24) & 0xff;
    buf[5] = (num >> 16) & 0xff;
    buf[6] = (num >> 8) & 0xff;
    buf[7] = num & 0xff;
    this->addToPack(buf, 8);
}

void Xpack::packAsATcmd(const char* atcmd,void* param,uint16_t len){
    //type frame AT command 0x08
    this->addToPack((uint8_t)0x08);
    //frame id
    this->addToPack((uint8_t)0x00);
    //at cmd
    this->addToPack(atcmd,2);
    //parameters
    this->addToPack((char*)param,len);
}

void Xpack::packAsRemoteATcmd(const uint32_t destAddr, const char* atcmd,void* param,uint16_t len){
    //type frame AT remote 0x17
    this->addToPack((uint8_t)0x17);
    //frame id
    this->addToPack((uint8_t)0xaa);
    //dest addr
    if(destAddr!=0x0000ffff && destAddr!=0x00000000){
        this->addToPack((uint32_t)0x0013A200);
    }else{
        this->addToPack((uint32_t)0x00000000);
    }
    this->addToPack(destAddr);
    //network addr
    this->addToPack((uint16_t)0xFFFE);
    //remote cmd option 0x01 - quiet, 0x02 - apply
    this->addToPack((uint8_t)0x02);
    //at cmd
    this->addToPack(atcmd,2);
    //parameters
    this->addToPack((char*)param,len);
}

void Xpack::setAddrRemoteATcmd(const uint32_t destAddr){
    static uint32_t buf;
    //this->setInPack(destAddr,6);
    if(destAddr==0x0000ffff || destAddr==0x00000000){
        this->setInPack((uint32_t)0x00000000,2);
    }else{
        this->setInPack((uint32_t)0x0013A200,2);
    }
    this->setInPack((char*)&destAddr,4,6);
    
}



void Xpack::printPack() {
    Xmsg::printf("-----\nType: 0x%x\n", this->type);
    Xmsg::printf("Length: 0x%x\n", this->length);
    Xmsg::printf("CRC: 0x%x %s\n", this->crc, checkCRC() ? "valid" : "no valid");
    Xmsg::printf("Data:\n");
    Xservice::printArrayToHexStr(this->data, this->length);
    Xmsg::printf("-----\n");
}
